#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>

#define MAX_LINE_LENGTH 8192

char dump_file_name[128] = {0};
char total_dump_text[10240] = {0};
const char* b64_suffix = ".b64";
const char* bz_suffix = ".bz2";

char *log_file_path = NULL;
char *symbols_path = "symbols";
char *outfile_path = NULL;
int opt;

// 检测依赖列表
const char* dependencies[] = {"base64", "bzip2", "minidump_stackwalk",NULL};

// 检测命令是否存在
int command_exists(const char* cmd) {
    char test_cmd[256];
    snprintf(test_cmd, sizeof(test_cmd), "command -v %s >/dev/null 2>&1", cmd);
    return system(test_cmd) == 0;
}


void remove_spaces(char *str) {
    // 初始化两个指针：src用于读取原字符串，dst用于写入非空格字符
    char *src = str;
    char *dst = str;
 
    while (*src) {
        // 如果当前字符不是空格，则复制到dst位置
        if (*src != ' ') {
            *dst = *src;
            dst++;
        }
        src++;
    }
    // 确保字符串以空字符结尾
    *dst = '\0';
}

void write_to_file(char *text){
    char file_name[136] = {0};
    snprintf(file_name,sizeof(file_name),"%s%s",dump_file_name,b64_suffix);
    // 以追加模式打开文件（文件不存在则创建）
    FILE *file = fopen(file_name, "a");
    if (!file) {
        perror("文件打开失败");
        return;
    }
    
    // 写入文件
    size_t len = strlen(text);
    size_t written = fwrite(text, 1, len, file);
    
    // 检查写入结果
    if (written != len) {
        perror("写入失败");
    }
 
    // 关闭文件
    fclose(file);
}

/**
 * 解析dump崩溃文件名信息
 */
bool parse_dump_file_name(const char* dump_start){
    // 2. 移动到路径开始位置（+12跳过"Dump path: "）
    dump_start += 12;
    
    // 3. 查找路径中的最后一个'/'
    const char *last_slash = strrchr(dump_start, '/');
    if (!last_slash) {
        fprintf(stderr, "Error: No path separator found\n");
        return false;
    }
    
    // 4. 提取文件名（最后一个'/'之后的内容）
    const char *filename_start = last_slash + 1;
    size_t filename_len = strlen(filename_start);
    
    // 5. 验证文件扩展名
    const char *ext = strrchr(filename_start, '.');
    if (!ext || strcmp(ext, ".dmp") != 0) {
        fprintf(stderr, "Error: Invalid file extension\n");
        return false;
    }
    memcpy(dump_file_name,filename_start,filename_len);

    printf("%s\n",dump_file_name);
    return true;
}

/**
 * 解析dump崩溃挂关键日志信息
 */
bool parse_cont(const char* pos,const char* target){
    // 计算有效内容的起始位置
    const char *start = pos + strlen(target);
    
    // 计算内容长度（直到字符串结尾）
    size_t length = strlen(start);
    
    // 动态分配内存存储结果
    char *result = (char*)malloc(length + 1);
    if (result == NULL) {
        perror("Memory allocation failed");
        return false;
    }
    
    // 复制内容到新内存区域
    strncpy(result, start, length);
    result[length] = '\0'; // 确保字符串正确终止
    
    remove_spaces(result);

    // 输出结果
    // printf("%s\n", result);
    write_to_file(result);
    
    // 释放内存
    free(result);
}

/**
 * 解码内容
 */
void decode_cont(){
    char cmd[512] = {0};
    // base64 decode
    snprintf(cmd,sizeof(cmd),"base64 --decode %s%s > %s%s",dump_file_name,b64_suffix,dump_file_name,bz_suffix);
    system(cmd);
    memset(cmd,0,sizeof(cmd));
    snprintf(cmd,sizeof(cmd),"rm %s%s",dump_file_name,b64_suffix);
    system(cmd);
    // bzip2 file
    memset(cmd,0,sizeof(cmd));
    snprintf(cmd,sizeof(cmd),"bzip2 -d %s%s",dump_file_name,bz_suffix);
    system(cmd);
    memset(cmd,0,sizeof(cmd));
    if(outfile_path == NULL){
        //output to console
        snprintf(cmd,sizeof(cmd),"minidump_stackwalk %s %s",dump_file_name,symbols_path);
    }else{
        //output ot file
        memset(cmd,0,sizeof(cmd));
        snprintf(cmd,sizeof(cmd),"echo  \\n\\n\"%s\" >> %s",dump_file_name,outfile_path);
        system(cmd);
        memset(cmd,0,sizeof(cmd));
        snprintf(cmd,sizeof(cmd),"minidump_stackwalk %s %s >> %s",dump_file_name,symbols_path,outfile_path);
        printf("%s.dmp file convert to text file ==> %s\n",dump_file_name,outfile_path);
    }
    system(cmd);
    memset(cmd,0,sizeof(cmd));
    snprintf(cmd,sizeof(cmd),"rm  %s",dump_file_name);
    system(cmd);
    printf("\n\n");
}

/**
 * 打印帮助信息
 */
void print_helper(){
    printf("Usage: dump_stack_to_text [options] <log-file> [symbol-path ...]\n\n");
    printf("Output a stack trace for the provided minidump\n\n");
    printf("Options:\n\n");
    printf("-f         Input log file path, not dir.\n");
    printf("-s         Symbols dir path.\n");
    printf("-o         Ouput dump stack to file with input name.\n\n");
    printf("Alse You can use with simple command below:\n\n");
    printf("dump_stack_to_text -f messages ;  // print dump stack to console,symbols must at current dir\n");
    printf("dump_stack_to_text -f messages -s /home/symbols;  // print dump stack to console\n");
    printf("dump_stack_to_text -f messages -s /home/symbols -o text.txt;  // output dump stack to text.txt\n");
    printf("dump_stack_to_text -f /home/cc/messages -s /home/symbols -o text.txt;  // output dump stack to text.txt\n");
}

int main(int argc,char** argv) {
    char output[MAX_LINE_LENGTH];
    const char* dump_path_name = "breakpad Dump path: ";
    const char* breakpad_key_log = "breakpad base64_encode:";
    const char* log_end = "breakpad convert_dump_to_syslog end";

    if(strcmp("-h",argv[1]) == 0){
        print_helper();
        return 0;
    }

    printf("正在检测系统依赖...\n");
    int all_found = 1;
    for (int i = 0; dependencies[i]; i++) {
        const char* dep = dependencies[i];
        if (!command_exists(dep)) {
            all_found = 0;
            printf("[ %s ] NOT FOUND,NEED INSTALL IT!\n",dep);
        } 
    }
 
    if (!all_found) {
        return 1;
    }


    // 解析选项参数
    while ((opt = getopt(argc, argv, "f:s:o:")) != -1) {
        switch (opt) {
            case 'f': log_file_path = optarg; break;
            case 's': symbols_path = optarg; break;
            case 'o': outfile_path = optarg; break;
            case '?': 
                fprintf(stderr, "无效参数或缺少参数值\n");
                return 1;
        }
    }
    

    if(log_file_path == NULL){
        printf("至少输入日志文件参数! eg ./dump_stack_to_text messages\n");
        return 1;
    }

    printf("参数解析结果：\n");
    printf("-f 路径: %s\n", log_file_path ? log_file_path : "未提供");
    printf("-s 路径: %s\n", symbols_path ? symbols_path : "未提供");
    printf("-o 路径: %s\n", outfile_path ? outfile_path : "未提供");

    // 执行grep命令
    char command[128] = {0};
    snprintf(command,sizeof(command),"grep -ae '%s' -e '%s' -e '%s' %s",dump_path_name,breakpad_key_log,log_end,log_file_path);
    FILE *fp = popen(command, "r");
    if (fp == NULL) {
        perror("popen failed");
        return 1;
    }
    // 读取输出并保存到动态数组
    while (fgets(output, MAX_LINE_LENGTH, fp) != NULL) {
        // 去除换行符
        output[strlen(output)-1] = '\0';
        // 定位"Dump path: "起始位置 文件名
        const char *dump = strstr(output, dump_path_name);
        if (dump) {
            parse_dump_file_name(dump);
        }

        // 关键日志
        const char *pos = strstr(output, breakpad_key_log);
        if (pos != NULL) {
            parse_cont(pos,breakpad_key_log);
        }
        // 日志结束，开始解析
        const char *end = strstr(output, log_end);
        if (end != NULL) {
            decode_cont();
        }
        
        // printf("%s\n", output);
        memset(output,0,sizeof(output));
    }

    pclose(fp);

    return 0;
}